﻿using System;
using System.Diagnostics;
using System.Globalization;
using Microsoft.SharePoint;

namespace PD.ShP.Code.DataModel
{
    /// <summary>
    /// Data context of Linq to SharePoint module.
    /// </summary>
    [DebuggerNonUserCode]
    public partial class SiteDataContext : Microsoft.SharePoint.Linq.DataContext
    {
        #region Fields

        /// <summary>
        /// Current site object.
        /// </summary>
        private SPSite defaultSite;

        /// <summary>
        /// Current web object.
        /// </summary>
        private SPWeb defaultWeb;

        /// <summary>
        /// Specifies if current Web/Site objects where opened or retrieved from context.
        /// </summary>
        private bool siteWasOpened, webWasOpened;

        /// <summary>
        /// Specifies if current object was disposed.
        /// </summary>
        private bool disposed = false;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the current web.
        /// </summary>
        public SPWeb CurrentWeb
        {
            get
            {
                if (this.defaultWeb == null)
                {
                    this.InitializeWeb(this.Web);
                }

                return this.defaultWeb;
            }
        }

        #endregion

        /// <summary>
        /// Gets the list and registers changed name if needed.
        /// </summary>
        /// <typeparam name="T">Type of the list to get.</typeparam>
        /// <param name="listName">Name of the list.</param>
        /// <returns>List entity object.</returns>
        public override Microsoft.SharePoint.Linq.EntityList<T> GetList<T>(string listName)
        {
            // register new name of the class if it was changed or multilingual
            this.TryRegisterListName<T>(listName);

            // gets the list by name
            return base.GetList<T>(listName);
        }

        /// <summary>
        /// Tries to register correct title of the list.
        /// </summary>
        /// <typeparam name="T">Type of the list to register.</typeparam>
        /// <param name="listName">Name of the list.</param>
        internal void TryRegisterListName<T>(string listName)
        {
            // gets url of the list by name
            var allLists = new Lists();
            var listurl = allLists.GetListUrlByTitle(listName);
            if (string.IsNullOrEmpty(listurl))
            {
                return;
            }

            // get title of the list in the invariant language
            var listTitle = Lists.GetInvariantListTitle(this.CurrentWeb, listurl);

            // if names are different then register invariant name
            if (listName != listTitle)
            {
                try
                {
                    this.RegisterList<T>(listTitle, listName);
                }
                catch (ArgumentException)
                {
                    // an exception can occure where there is no such list
                    // TODO log exception here
                }
            }
        }

        #region IDisposable

        /// <summary>
        /// Releases all unmanaged resources used by the <see cref="T:Microsoft.SharePoint.Linq.DataContext"/> object and possibly also the managed resources as specified.
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources, false to release only unmanaged resources.</param>
        protected override void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                try
                {
                    if (disposing)
                    {
                        // free managed resources
                        if (this.defaultWeb != null && this.webWasOpened)
                        {
                            this.defaultWeb.Dispose();
                        }

                        if (this.defaultSite != null && this.siteWasOpened)
                        {
                            this.defaultSite.Dispose();
                        }
                    }

                    this.defaultWeb = null;
                    this.defaultSite = null;
                    this.disposed = true;
                }
                finally
                {
                    // Call Dispose on your base class.
                    base.Dispose(disposing);
                }
            }
        }

        #endregion

        /// <summary>
        /// Initializes the web.
        /// </summary>
        /// <param name="url">The URL of the data context.</param>
        private void InitializeWeb(string url)
        {
            if (this.disposed)
            {
                throw new ObjectDisposedException("Cannot initialized members if object was disposed.");
            }

            if (SPContext.Current != null)
            {
                this.defaultSite = SPContext.Current.Site;
                this.siteWasOpened = false;
                if (SPContext.Current.Web.Url == url)
                {
                    this.defaultWeb = SPContext.Current.Web;
                    this.webWasOpened = false;
                }
                else
                {
                    this.defaultWeb = this.defaultSite.OpenWeb(new Uri(url).PathAndQuery);
                    this.webWasOpened = true;
                }
            }
            else
            {
                this.defaultSite = new SPSite(url);
                this.siteWasOpened = true;
                this.defaultWeb = this.defaultSite.OpenWeb(new Uri(url).PathAndQuery);
                this.webWasOpened = true;
            }

            if (!this.defaultWeb.Exists)
            {
                throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Cannot find the specified web object: {0}.", url));
            }
        }
    }
}
